#region imported Namespaces
using System;
using System.Reflection;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Web;

using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using DataAccess = PHSRAG.Utility.DataAccess;
using PHSRAG.Insight21.CDS;
using PHSRAG.Security.Enumerations;
#endregion

namespace PHSRAG.Insight21.Humans
{
	/// <summary>
	/// Represents a human protocol.
	/// </summary>
	public class Protocol : BO.SmartBusinessObject, ICloneable
	{
		#region Constants
		private const string	protocolKey = "protocol_5547A6D0-9766-487b-A051-B11113D4C326";
		private const string	ProtocolDataTableName = "Protocol";
		private const int		ProtocolTableIndex = 0;
		private const int		MaxSponsorLength = 30;
		private const string	ChangeCommand = "Protocol has changed";
		#endregion

		#region Instance Variables
		private string protocolNumber;
		private string title;
		private string version;
		private int orgId;
		private int piPersonId;
		private int overallStatusId;
		private int subStatusId;
		private int fwkResourceId;
		private int accessLevel;
		private int creatorId;
		private bool isLocked;
		private bool detailsLoaded;
		private bool isLegacy;
        private string moduleType;
        private string speciesCode;
        private string speciesMeaning;
        private DateTime protocolPeriodStartDate;
        private DateTime protocolPeriodEndDate;
        private int numberOfAnimals;
        private bool radioIsotope;
        private bool biologicalAgent;
        private bool toxicChem;
        private bool tissueTest;
        
		private string responsibleIRB;
		private int	   responsibleIRBId;
		private string piName;
		private string lastModifierName;
		private int sponsorId;
		private string sponsorName;
        private string protocolAbstract;
		private string overallStatus;
        private DateTime submissionDate = DateTime.MinValue;
		private string lastModifiedDate;
		private string expirationDate;
		private string pendingIRB;
		private string administratorName;
		private string adminEmail;
		private string adminPhone;
		private int	   administratorId;
		private int	   consentFormCount;
		private string approvalDate;
		private string protocolTypeCode;
		private ArrayList studyStaffList;
        private ArrayList nonStudyStaffList;
        private ArrayList protocolDocuments;
		private int    rexProtocolId;
		private int    isInterventionStudy;
		private int		administratorDomainUserId;
		private ArrayList protocolProcesses;
		private DataSet dataContainer;
		private System.ComponentModel.EventHandlerList events = new EventHandlerList();
        private ArrayList includeEditableList;

		#endregion

		#region Properties
		/// <summary>
		/// Is this protocol an intervention or non-intervention study? Set in IR config script.
		/// </summary>
		public int IsInterventionStudy
		{
			get { return isInterventionStudy; }
			set { SetDirty ( ref isInterventionStudy, value ); }
		}

		/// <summary>
		/// Gets the StudyStaff for the Protocol, and returns an ArrayList of StaffPerson objects that wrap
		/// the StudyStaff dataContainer's rows.
		/// </summary>
		public virtual ArrayList StudyStaffList
		{
			get 
			{
				if (studyStaffList == null)
				{
					StudyStaff studyStaff = new StudyStaff(CachePolicy);
					// pass in 0 for ProtocolProcessId
					studyStaff.Load(DataAccess.GetConnectionString(), Id, 0);
					studyStaffList = studyStaff.GetStudyStaffList();					
				}
				return studyStaffList;
			}
			set { studyStaffList = value; }			
		}

        /// <summary>
        /// Collection of NonStaffPerson objects in an ArrayList.  It represents users with 
        /// permissions to the protocol, but not staffed to it.
        /// </summary>
        public virtual ArrayList NonStudyStaff
        {
            get
            {
                if (nonStudyStaffList == null)
                {
                    NonStudyStaff nonStudyStaff = new NonStudyStaff(CachePolicy);
                    nonStudyStaff.Load(DataAccess.GetConnectionString(), Id, 0);
                    nonStudyStaffList = nonStudyStaff.GetNonStudyStaffList();
                }
                return nonStudyStaffList;
            }
            set { nonStudyStaffList = value; }
        }
        
        /// <summary>
		/// bool to determine if the Protocol is WIP or not.
		/// </summary>
		public bool WorkInProgress 
		{ 
			get { return (ProtocolNumber == null || ProtocolNumber == string.Empty); }
		}

		/// <summary>
		/// Cache Key uniquely identifies this object within the cache.
		/// </summary>
		/// <returns>the unique string for this object.</returns>
		public override string CacheKey()
		{
			return protocolKey;
		}

		/// <summary>
		/// Key used to place and/or retrieve this object from the cache.
		/// </summary>
		public static string ProtocolKey
		{
			get { return protocolKey; }
		}

		/// <summary>
		/// The Identity value of the matching record in Rex of that Protocol in Rex, after it
		/// gets moved over to Rex during the Submission. Note that this may not always be populated
		/// for legacy protocols.
		/// </summary>
		public int RexProtocolId
		{
			get { return rexProtocolId; }
			set { SetDirty( ref rexProtocolId, value ); }
		}

		
		/// <summary>
		/// ResourceId of the protocol
		/// </summary>
		public int FwkResourceId
		{
			get { return fwkResourceId; }
		}

		/// <summary>
		/// Gets the access level the logged-in user has to the protocol
		/// </summary>
		public int AccessLevel
		{
			get { return accessLevel; }
		}

		/// <summary>
		/// Gets the long title of the Protocol
		/// </summary>
		public string Title
		{
			get { return title; }
			set { SetDirty( ref title, value ); }
		}

		/// <summary>
		/// A shortened version of the long Title (if its longer than 30 chars, it is truncated to 27 chars + ...)
		/// </summary>
		public string TruncatedTitle
		{
			get
			{
				string longTitle = Title;
				return (longTitle.Length > Constants.MaxTitleLength) ? (longTitle.Substring(0, Constants.MaxTitleLength - 3) + "...") : longTitle;
			}
		}

		/// <summary>
		/// The number of the Protocol as assigned by REX
		/// </summary>
		public string ProtocolNumber
		{
			get { return protocolNumber;	}
			set { SetDirty ( ref protocolNumber, value );}
		}

		/// <summary>
		/// PI's primary organization
		/// </summary>
		public int OrganizationId
		{
			get { return orgId;	}
			set { SetDirty( ref orgId, value );}
		}

		/// <summary>
		/// Gets the name of the Principal Investigator on the Protocol in Last Name, First Name format
		/// Sets the name of the Principal Investigator on the Protocol but this is purely for display 
		/// purposes in the detail header, and will not persist in the data repository
		/// </summary>
		public string PIName
		{
			get { return piName; }
			set { piName = value; }
		}

		/// <summary>
		/// Gets the name of the person who last modified the protocol (if modified)
		/// </summary>
		public string LastModifierName
		{
			get	{ return lastModifierName;	}
		}

		/// <summary>
		/// Gets the name of the sponsor on the protocol
		/// </summary>
		public string SponsorName
		{
			get	{ return sponsorName; }
		}

        /// <summary>
        /// Gets the name of the sponsor on the protocol
        /// </summary>
        public string ProtocolAbstract
        {
            get { return protocolAbstract; }
        }

		/// <summary>
		/// Gets a shortened version of the SponsorName (truncates to 30 chars if longer)
		/// </summary>
		public string TruncatedSponsorName
		{
			get
			{
				string longSponsor = SponsorName;
				if ( longSponsor != null )
				{
					return (longSponsor.Length > MaxSponsorLength) ? 
						(longSponsor.Substring(0, MaxSponsorLength - 3) + "...") : longSponsor;
				}
				else
					return string.Empty;
			}
		}	 

		/// <summary>
		/// Gets or sets the id of the sponsor on the protocol
		/// </summary>
		public int SponsorId
		{
			get { return sponsorId; }
			set { SetDirty (ref sponsorId, value); }
		}
		/// <summary>
		/// Gets the combined overall and project status of the protocol
		/// </summary>
		public string OverallStatus
		{
			get	{ return overallStatus;	}
			set { SetDirty( ref overallStatus, value); }
		}

		/// <summary>
		/// Gets and sets the date the Protocol was submitted to the IRB
		/// </summary>
		public DateTime SubmissionDate
		{
			get	{ return submissionDate; }
			set { SetDirty( ref submissionDate, value); }
		}

		/// <summary>
		/// Gets the date the protocol was last modified (if any)
		/// </summary>
		public string LastModifiedDate
		{
			get	{ return lastModifiedDate; }
		}

		/// <summary>	
		/// Whether a protocol has been locked (i.e. changes cannot be made to it)
		/// </summary>
		public bool IsLocked
		{
			get { return isLocked; }
			set { SetDirty( ref isLocked, value); }
		}

		/// <summary>
		/// Gets whether or not the protocol is a legacy protocol (brought over from REX)
		/// </summary>
		public bool IsLegacy
		{
			get { return isLegacy; }
		}

		/// <summary>
		/// Gets the date the protocol expires
		/// </summary>
		public string ExpirationDate
		{
			get	{ return expirationDate; }		
		}

		/// <summary>
		/// Gets any items that are pending IRB approval
		/// </summary>
		public string PendingIRB
		{
			get	{ return pendingIRB; }
		}
		/// <summary>
		/// Gets the count(number of consent Forms available for a protocol)
		/// </summary>
		public int ConsentFormCount
		{
			get	{ return consentFormCount; }
		}


		/// <summary>
		/// Gets all the names of protocol documents tied to the protocol
		/// </summary>
		public string ProtocolDocumentNames
		{
			get 
			{ 
				return ProtocolDocument.GetProtocolDocumentNames(CachePolicy, Id); 
			}
		}

		/// <summary>
		/// Gets or sets the ProtocolDocuments collection associated with the Protocol
		/// </summary>
		public ArrayList ProtocolDocuments
		{
			get 
			{
				if (protocolDocuments == null)
				{
					protocolDocuments = ProtocolDocument.GetProtocolDocuments(Id, CachePolicy);
				}
				return protocolDocuments;
			}
			set
			{ protocolDocuments = value; }
		}

        /// <summary>
        /// Gets or sets the module associated with this Protocol (animals or humans)
        /// </summary>
        public string ModuleType
        {
            get { return moduleType; }
            set { SetDirty(ref moduleType, value); }
        }

        /// <summary>
        /// Gets or sets the species code of this protocol (applicable to Animals)
        /// </summary>
        public string SpeciesCode
        {
            get { return speciesCode; }
            set { SetDirty(ref speciesCode, value); }
        }

        /// <summary>
        /// Gets or sets the period start date of this protocol (applicable to Animals)
        /// </summary>
        public DateTime ProtocolPeriodStartDate
        {
            get { return protocolPeriodStartDate; }
            set { SetDirty(ref protocolPeriodStartDate, value); }
        }

        /// <summary>
        /// Gets or sets the period end date of this protocol (applicable to Animals)
        /// </summary>
        public DateTime ProtocolPeriodEndDate
        {
            get { return protocolPeriodEndDate; }
            set { SetDirty(ref protocolPeriodEndDate, value); }
        }

        /// <summary>
        /// Gets or set the number of animals associated with this protocol 
        /// (applicable to Animals)
        /// </summary>
        public int NumberOfAnimals
        {
            get { return numberOfAnimals; }
            set { SetDirty(ref numberOfAnimals, value); }
        }

        /// <summary>
        /// Gets or sets an indicator of whether this protocol uses radio isotope(s)
        /// (applicable to Animals)
        /// </summary>
        public bool RadioIsotope
        {
            get { return radioIsotope; }
            set { SetDirty(ref radioIsotope, value); }
        }

        /// <summary>
        /// Gets or sets an indicator of whether this protocol uses biological agents
        /// (applicable to Animals)
        /// </summary>
        public bool BiologicalAgent
        {
            get { return biologicalAgent; }
            set { SetDirty(ref biologicalAgent, value); }
        }

        /// <summary>
        /// Gets or sets an indicator of whether this protocol uses toxic chemicals
        /// (applicable to Animals)
        /// </summary>
        public bool ToxicChem
        {
            get { return toxicChem; }
            set { SetDirty(ref toxicChem, value); }
        }

        /// <summary>
        /// Gets or sets an indicator of whether this protocol uses tissue testing
        /// (applicable to Animals)
        /// </summary>
        public bool TissueTest
        {
            get { return tissueTest; }
            set { SetDirty(ref tissueTest, value); }
        }

        #region ProtocolDetailsHeader extra info

        /// <summary>
        /// Gets the RefCode meaning of the SpeciesCode of this protocol (applicable to animals)
        /// </summary>
        public string SpeciesMeaning
        {
            get { return speciesMeaning; }
        }

		/// <summary>
		/// Gets the name of the Protocol Administrator
		/// </summary>
		public string AdministratorName
		{
			get	{ return administratorName; }
		}

		/// <summary>
		/// The AdmPersonId of the HRO Protocol Administrator. This is
		/// typically set when the Org is set of the Protocol.
		/// </summary>
		public int AdministratorId
		{
			get { return administratorId; }
			set { SetDirty ( ref administratorId, value ); }
		}
		
		/// <summary>
		/// Gets the email of the Administrator
		/// </summary>
		public string Email
		{
			get	{ return adminEmail; }
		}
		
		/// <summary>
		/// Gets the phone of the Administrator
		/// </summary>
		public string Phone
		{
			get	{ return adminPhone; }
		}

		/// <summary>
		/// Gets the date the protocol was approved
		/// </summary>
		public string ApprovalDate
		{
			get	{ return approvalDate; }		
		}
		
		/// <summary>
		/// The name of the IRB responsible for the protocol
		/// </summary>
		public string ResponsibleIRB
		{
			get	{ return responsibleIRB;}
			set { SetDirty ( ref responsibleIRB, value ); }
		}

		/// <summary>
		/// The FwkDomainOrganizationId of the Responsible Institution
		/// for this protocol.
		/// </summary>
		public int ResponsibleIRBId
		{
			get { return responsibleIRBId; }
			set { SetDirty ( ref responsibleIRBId, value ); }
		}

		/// <summary>
		/// Gets the version of the Protocol (sponsor-given title)
		/// </summary>
		public string Version
		{
			get	{ return version;	}
			set { SetDirty( ref version, value ); }
		}

		/// <summary>
		/// AdmPerson ID of the PI
		/// </summary>
		public int PIPersonId
		{
			get	{ return piPersonId; }
			set{ SetDirty ( ref piPersonId, value ); }
		}

		/// <summary>
		/// DmnUserId of the person creating the protocol. This is necessary to grant access to this 
		/// person in the Save of the stored procedure
		/// </summary>
		public int CreatorId
		{
			get { return creatorId; } 
			set { SetDirty( ref creatorId, value); }
		}

		/// <summary>
		/// Overall status Id
		/// </summary>
		public int OverallStatusId
		{
			get { return overallStatusId; }
			set { SetDirty( ref overallStatusId, value );  }
		}
		
		/// <summary>
		/// Id of the Protocol's substatus (e.g. 'Enrolling Subjects')
		/// </summary>
		public int SubStatusId
		{
			get { return subStatusId; }
			set { SetDirty( ref subStatusId, value );  }
		}


		/// <summary>
		/// The Reference Code of the Protocol Type (currently the EIRB only 
		/// supports new and 
		/// </summary>
		public string ProtocolTypeCode
		{
			get { return protocolTypeCode; }
			set { SetDirty( ref protocolTypeCode, value ); }
		}


		/// <summary>
		/// Collection of ProtocolProcesses for a given protocol.
		/// </summary>
		public ArrayList ProtocolProcesses
		{
			get 
			{
				if ( protocolProcesses == null )
					protocolProcesses = Protocol.GetProtocolProcesses( Id, CachePolicy );
				return protocolProcesses; }
		
			set { protocolProcesses = value; }
		}
		
		/// <summary>
		/// DataContainer
		/// </summary>
		public DataSet DataContainer
		{
			get { return dataContainer; }
			set { dataContainer = value; }
		}

		/// <summary>
		/// The FwkDomainUserId of the HRO Protocol Administrator. This is
		// </summary>
		public int AdministratorDomainUserId
		{
			get { return administratorDomainUserId; }
			set { SetDirty ( ref administratorDomainUserId, value ); }
		}

      
        /// <summary>
        /// Gets the InfoEd Proposal Number
        /// </summary>
        public string InfoEdProposalNumber
        {
            get
            {
                return GetProposalNumberByProtocolNumber(this.ProtocolNumber); 
            }
            
        }
	
		#endregion

        /// <summary>
        /// Gets the value of the Manage permission based on the protocol module type
        /// </summary>
        public int ManagePermission
        {
            get
            {
                if (string.IsNullOrEmpty(ModuleType))
                    throw new Exception( "ModuleType required to access ManagePermission" );
                else
                    return (ModuleType == Constants.ModuleType.Humans) ? (int)AccessRightBit.ManageProtocolInformation : (int)AccessRightBit.ManageAnimalsProtocolInformation;
            }
        }

        /// <summary>
        /// Get the value of the Edit permission based on the protocl module type
        /// </summary>
        public int EditPermission
        {
            get
            {
                if (string.IsNullOrEmpty(ModuleType))
                    throw new Exception("ModuleType required to access EditPermission");
                else
                    return (ModuleType == Constants.ModuleType.Humans) ? (int)AccessRightBit.EditProtocolInformation : (int)AccessRightBit.EditAnimalsProtocolInformation;
            }
        }

        /// <summary>
        /// Get the value of the View permission based on the protocl module type
        /// </summary>
        public int ViewPermission
        {
            get
            {
                if (string.IsNullOrEmpty(ModuleType))
                    throw new Exception("ModuleType required to access ViewPermission");
                else
                    return (ModuleType == Constants.ModuleType.Humans) ? (int)AccessRightBit.ViewProtocolInformation : (int)AccessRightBit.ViewAnimalsProtocolInformation;
            }
        }

        /// <summary>
        /// get the list from db of IncludeEditable value
        /// </summary>
        public ArrayList IncludeEditableList
        {
            get
            {
                includeEditableList = GetincludeEditableList(Utility.DataAccess.ConnectionString, Id);
                if (includeEditableList == null)
                    includeEditableList = new ArrayList();

                return includeEditableList;
            }
        } 

        #endregion Properties
		
		#region Constructor

		/// <summary>
		/// Constructs a Protocol object and sets its dataRow to the passed in row
		/// </summary>
		/// <param name="cachePolicy">the policy used to cache the Protocol object</param>
		public Protocol(ICachePolicy cachePolicy, DataRow row) : base(cachePolicy)
		{
			if( row != null )
				PopulateDataMembers(row);
		}

		/// <summary>
		/// Initializes the base class with the specified cache policy
		/// </summary>
		/// <param name="cachePolicy">cachepolicy for the object.</param>
		public Protocol(ICachePolicy cachePolicy ) : base(cachePolicy)
		{
		}
		#endregion Constructor

        object ICloneable.Clone()
        {
            return Clone(true, true, 0);
        }

        public Protocol Clone( bool includeStaff, bool includeAttachments, int creatorId )
        {
            Protocol clone;
            try
            {
                clone = (Protocol)MemberwiseClone();
                clone.Id = 0;
                clone.IsClone = true;
                clone.Dirty = true;
                clone.ProtocolNumber = null;
                clone.RexProtocolId = 0;
                clone.ProtocolPeriodStartDate = DateTime.MinValue;
                clone.ProtocolPeriodEndDate = DateTime.MinValue;
                clone.SubmissionDate = DateTime.MinValue;
                if ( creatorId != 0 )
                    clone.CreatorId = creatorId;
                clone.OverallStatusId = (int)Constants.ProtocolOverallStatus.WorkInProgress;
                clone.OverallStatus = Constants.ProtocolOverallStatusText.WorkInProgress;
                clone.IsLocked = false;
                clone.RexProtocolId = 0;

               

                clone.protocolProcesses = new ArrayList();
                foreach (ProtocolProcess protocolProcess in ProtocolProcesses)
                {
                    if ((clone.ModuleType == Constants.ModuleType.Humans && protocolProcess.Type == Constants.ProtocolProcessType.InitialReview) ||
                        clone.ModuleType == Constants.ModuleType.Animals && protocolProcess.Type == Constants.AnimalProtocolProcessType.InitialReview)
                    {
                        ProtocolProcess clonedProcess = protocolProcess.Clone(includeStaff, includeAttachments);
                        clonedProcess.Status = Constants.ProtocolProcessStatus.WorkInProgress;
                        clonedProcess.Locked = false;
                        clonedProcess.LastLockedVersion = 0;
                        clonedProcess.LastModifiedByUserId = creatorId;
                        clonedProcess.ReceivedDate = DateTime.MinValue;
                        clonedProcess.BeenSubmitted = false;
                        clonedProcess.HasBeenLocked = false;
                        clonedProcess.SubmitterId = creatorId;
                        clone.protocolProcesses.Add(clonedProcess);
                    }
                }

                clone.Save(DataAccess.ConnectionString);

                NonStudyStaff nonStudyStaff = new NonStudyStaff(CachePolicy);
                nonStudyStaff.Load(DataAccess.GetConnectionString(), Id, 0);
                ArrayList nonStudyStaffList = nonStudyStaff.GetNonStudyStaffList();
                foreach (NonStaffPerson nonStaffPerson in nonStudyStaffList)
                {
                    NonStaffPerson cloneNonStudyStaff = nonStaffPerson.Clone();
                    cloneNonStudyStaff.ProtocolId = clone.Id;
                    cloneNonStudyStaff.Save(DataAccess.ConnectionString);
                }
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog(
                    "Protocol.Clone", ex, true, Utility.ExceptionReport.ReportType.Error);
                throw;
            }

            return clone;
        }

		#region Overrides

		/// <summary>
		/// This method is used to load remaining details that aren't initially provided by the protocol
		/// object when constructed (using the dataRow provided).  It loads more details from the database
		/// and overwrites its dataRow reference to a new row in its own dataContainer
		/// </summary>
		/// <param name="connectionString">Connection string to the database<</param>
		/// <param name="args">additional arguments</param>
		public override void Load(string connectionString, params object[] args)
		{
			if(args.Length != 1) 
				throw new Exception("Protocol Load expects protocol ID.");

			DataContainer = Utility.DataAccess.GetDataSet(
				connectionString,
				"GetProtocolDetails", 
				Utility.DataAccess.BuildSqlParameterArray("@protocolId" ,SqlDbType.Int, Convert.ToInt32( args[0])));

			detailsLoaded = true;
            if (DataContainer.Tables[0].Rows.Count > 0)
			{
				DataContainer.Tables[ProtocolTableIndex].TableName = ProtocolDataTableName;
				PopulateDataMembers ( DataContainer.Tables[0].Rows[0] );
			}
			else 
                throw new Exception("no tables were returned in the resultset");
		}

        /// <summary>
        /// Find the type of protocol
        /// </summary>
        /// <param name="connectionString">Connection string to the database<</param>
        /// <param name="args">additional arguments</param>
        public static DataTable GetProtocolDetailsForContract(string connectionString, params object[] args)
        {
           

            DataSet ds = Utility.DataAccess.GetDataSet(
                connectionString,
                "GetProtocolModuleName",
                Utility.DataAccess.BuildSqlParameterArray("@protocolNumber", SqlDbType.VarChar, args[0]
                                                          , "@domainuserid", SqlDbType.Int, args[1]
                
                ));


            if (ds.Tables.Count > 0)
            {
                return ds.Tables[0];
            }

            else throw new Exception("no tables were returned in the resultset");
        }

        /// <summary> 
        /// Find the type of protocol 
        /// </summary> 
        /// <param name="connectionString">Connection string to the database<</param> 
        /// <param name="args">additional arguments</param> 
        public static ArrayList GetincludeEditableList(string connectionString, params object[] args) 
        {
            ArrayList incList = null;   

            DataSet ds = Utility.DataAccess.GetDataSet(
                connectionString, 
                "GetProtocolDocumentIncludeEditableList",
                Utility.DataAccess.BuildSqlParameterArray("@protocolID", SqlDbType.Int, args[0])
            );                   

            if (ds.Tables.Count > 0) 
            { 
                incList = new ArrayList();
                foreach (DataRow row in ds.Tables[0].Rows) 
                {                                                                    
                    incList.Add(row[0]); 
                } 

            } 
            
            return incList;  
        }                                           


		/// <summary>
		/// Determines whether the protocol needs to be saved, based on whether anything on the Object has changed.
		/// </summary>
		/// <returns></returns>
		public override bool IsDirty()
		{
			if( dirty )
				return true;

			if (protocolProcesses != null)
			{
				for( int i = 0; i < this.protocolProcesses.Count; ++i)
				{
					if( ((ProtocolProcess)this.protocolProcesses[i]).IsDirty() )
						return true;
				}
			}

            if (studyStaffList != null)
            {
                for (int i = 0; i < this.studyStaffList.Count; ++i)
                {
                    if (((StaffPerson)this.studyStaffList[i]).IsDirty())
                        return true;
                }
            }
            
            if (protocolDocuments != null)
			{
				for(int i=0, j=protocolDocuments.Count; i<j ; ++i)
				{
					if (((ProtocolDocument)protocolDocuments[i]).IsDirty())
						return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Override Delete, which currently Marks the Protocol for Deletion (doesn't run a DELETE SQL 
		/// command - we'll implement this later -Cascading deletes??) This way, a users work can be recovered.
		/// </summary>
		/// <param name="connectionString"></param>
		protected override void DeleteObject(SqlTransaction sqlTransaction)
		{
			DataAccess.ExecuteNonQuery ( sqlTransaction.Connection, sqlTransaction, "DeleteProtocol",
					DataAccess.BuildSqlParameterArray (
						"@id",		SqlDbType.Int,			Id ) );
		}

		protected override void SaveObject(SqlTransaction sqlTransaction)
		{
            bool newProtocol = (Id == 0);
			// the stored procedure will determine whether to insert or update an existing Protocol.
			Id = Convert.ToInt32( DataAccess.ExecuteScalar( sqlTransaction.Connection, sqlTransaction, "SaveProtocol", 
				DataAccess.BuildSqlParameterArray(
				"@id",				SqlDbType.Int,			Id,
				"@title",			SqlDbType.VarChar,		title,
				"@shortTitle",		SqlDbType.VarChar,		TruncatedTitle,
				"@protocolNumber",	SqlDbType.VarChar,		(protocolNumber == string.Empty) ? (object)DBNull.Value : (object)ProtocolNumber,
                "@PIPersonId",      SqlDbType.Int,          (piPersonId == 0) ? (object)DBNull.Value : (object)piPersonId,
                "@overallStatusId",	SqlDbType.Int,			overallStatusId,
                "@subStatusId",		SqlDbType.Int,			(subStatusId == 0) ? (object)DBNull.Value : (object)subStatusId,
				"@isLocked",		SqlDbType.Bit,			isLocked,
				"@version",			SqlDbType.VarChar,		version,
                "@primaryOrgId",    SqlDbType.Int, (OrganizationId == 0) ? (object)DBNull.Value : (object)OrganizationId,
				"@creatorId",		SqlDbType.Int,			creatorId,
				"@admSponsorId",	SqlDbType.Int,			(sponsorId == 0) ? DBNull.Value : (object)sponsorId,
				"@protocolType",	SqlDbType.VarChar,		protocolTypeCode,
				"@administratorId",	SqlDbType.Int,			(administratorId == 0) ? (object)DBNull.Value : (object)administratorId,
				"@responsibleIRBOrgId", SqlDbType.Int,		(responsibleIRBId == 0) ? (object)DBNull.Value : (object)responsibleIRBId,
				"@submissionDate", SqlDbType.DateTime,      (submissionDate == DateTime.MinValue) ? (object)DBNull.Value : (object)submissionDate,
				"@rexProtocolId",  SqlDbType.Int,           (rexProtocolId == 0 ) ? (object)DBNull.Value : (object)rexProtocolId,
				"@isInterventionStudy", SqlDbType.Int,	    IsInterventionStudy,
                "@module",          SqlDbType.Char,         moduleType,
                "@speciesCode",     SqlDbType.VarChar,      (SpeciesCode == null) ? (object)DBNull.Value : SpeciesCode,
                "@protocolPeriodStartDate", SqlDbType.DateTime, (ProtocolPeriodStartDate == DateTime.MinValue) ? (object)DBNull.Value : ProtocolPeriodStartDate,
                "@protocolPeriodEndDate", SqlDbType.DateTime, (ProtocolPeriodEndDate == DateTime.MinValue) ? (object)DBNull.Value : ProtocolPeriodEndDate, 
                "@numberOfAnimals", SqlDbType.Int,          NumberOfAnimals,
                "@radioIsotope",    SqlDbType.Bit,          RadioIsotope,
                "@biologicalAgent", SqlDbType.Bit,          BiologicalAgent,
                "@toxicChem",       SqlDbType.Bit,          ToxicChem,
                "@tissueTest",      SqlDbType.Bit,          TissueTest)));
//
            if (newProtocol)
            {
                Person creatorPerson = Person.GetPersonByUserId(DataAccess.ConnectionString, CachePolicy, CreatorId);
                NonStaffPerson creator = new NonStaffPerson(CachePolicy);
                creator.AdmPersonId = creatorPerson.Id;
                creator.ProtocolId = Id;
                creator.PermissionLevel = ManagePermission;
                creator.Save(sqlTransaction);
            }

			// save its collection(s)...
			if ( protocolProcesses != null )
			{
				for( int i = 0; i < this.protocolProcesses.Count; ++i)
				{
					ProtocolProcess process = (ProtocolProcess)this.protocolProcesses[i];
					process.ProtocolId = Id;
					process.Save( sqlTransaction );
				}
			}

			if (protocolDocuments != null)
			{
				ArrayList deletedPDsList = new ArrayList();
				foreach (ProtocolDocument pd in protocolDocuments)
				{
					pd.ProtocolId = Id;
                    if (Utility.Settings.GetKeyValue("SaveToSharePoint", false))
                    {
                        pd.ElectronicFile.FolderHierarchy = ProtocolDocument.CreateFolderHierarchy(Id.ToString(), pd.ElectronicFile.Id, pd.ElectronicFile.Name, true); 
                        pd.ElectronicFile.IsSharePointDocument = true;
                    }
					pd.Save(sqlTransaction);
					if (pd.MarkForDeletion)
						deletedPDsList.Add(pd);
				}

				//remove deleted items from items collection
				foreach (ProtocolDocument deletedPD in deletedPDsList)
					protocolDocuments.Remove(deletedPD);
			}

            if (studyStaffList != null)
            {
                for(int i=0, j=StudyStaffList.Count; i<j; ++i)
                {
                    StaffPerson staffPerson = (StaffPerson)StudyStaffList[i];
                    staffPerson.ProtocolId = Id;
                    staffPerson.Save(sqlTransaction);
                }
            }

            if (nonStudyStaffList != null)
            {
                for (int i = 0, j = nonStudyStaffList.Count; i < j; ++i)
                {
                    NonStaffPerson staffPerson = (NonStaffPerson)nonStudyStaffList[i];
                    staffPerson.ProtocolId = Id;
                    staffPerson.Save(sqlTransaction);
                }
            }

			/* Publish the changes */
			ChangeEventHandler handler = (ChangeEventHandler)events[ChangeCommand];
			if (handler != null)
				handler();					
		}

		/// <summary>
		/// Cache this instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(protocolKey, this);
		}

		/// <summary>
		/// Remove this instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(protocolKey);
		}
		#endregion Overrides

		#region Private Methods

		/// <summary>
		/// Populates member variables from a the passed-in DataRow. This is called in the constructor that takes a 
		/// DataRow, as well as the Load method of an object.
		/// </summary>
		/// <param name="row">row whose data is used to populate instance variables</param>
		private void PopulateDataMembers ( DataRow dataRow )
		{
			object o = dataRow["ProtocolNumber"];
			protocolNumber = (o == DBNull.Value) ? string.Empty : (string)o;	
			Id = (int)dataRow["Id"];
			title = (string)dataRow["Title"];
			o = dataRow["OrganizationId"];
			orgId = (o == DBNull.Value) ? 0 : (int)o;	
			if (dataRow.Table.Columns.Contains("ConsentCount"))
			{				
				o = dataRow["ConsentCount"];
				consentFormCount = (o == DBNull.Value) ? 0 : (int)o;
			}			
			o = dataRow["Version"];
			version = (o == DBNull.Value) ? string.Empty : (string)o;	
			o = dataRow["PIName"];
			piName = (o == DBNull.Value) ? string.Empty : (string)o;
			o = dataRow["SponsorName"];
			sponsorName =  (o == DBNull.Value) ? string.Empty : (string)o;	
			overallStatus = (string)dataRow["OverallStatus"];
			// if this protocol is being loaded through the ExemptProtocolSummaries, this column won't exist
			if (dataRow.Table.Columns.Contains("PendingIRB"))
			{
				o = dataRow["PendingIRB"];
				pendingIRB = (o == DBNull.Value) ? "" : (string)o;	
			}
			o = dataRow["ExpirationDate"];
			expirationDate = (o == DBNull.Value) ? string.Empty : ((DateTime)o).ToString("MM/dd/yyyy");
			isLocked = (bool)dataRow["IsLocked"];
			if (dataRow.Table.Columns.Contains("IsLegacy"))
			{
				isLegacy = (bool)dataRow["IsLegacy"];
			}

			if (dataRow.Table.Columns.Contains("AccessLevel"))
				accessLevel = (int)dataRow["AccessLevel"];

			if (dataRow.Table.Columns.Contains("PIPersonId"))
			{
				o = dataRow["PIPersonId"];
				piPersonId = (o == DBNull.Value) ? 0 : (int)o;
			}

			if (dataRow.Table.Columns.Contains("RexProtocolId"))
			{
				o = dataRow["RexProtocolId"];
				rexProtocolId = ( o == DBNull.Value ) ? 0 : (int)o;
			}

			if (dataRow.Table.Columns.Contains("IsInterventionStudy"))
			{
				IsInterventionStudy = (int)dataRow["IsInterventionStudy"];
			}
            if (dataRow.Table.Columns.Contains("ModuleType"))
            {
                o = dataRow["ModuleType"];
                moduleType = (o == DBNull.Value) ? string.Empty : (string)o;
            }
            if (dataRow.Table.Columns.Contains("SpeciesCode"))
            {
                o = dataRow["SpeciesCode"];
                speciesCode = (o == DBNull.Value) ? string.Empty : (string)o;
            }
            if (dataRow.Table.Columns.Contains("SpeciesMeaning"))
            {
                o = dataRow["SpeciesMeaning"];
                speciesMeaning = (o == DBNull.Value) ? string.Empty : (string)o;
            }
            if (dataRow.Table.Columns.Contains("ProtocolAbstract"))
            {
                o = dataRow["ProtocolAbstract"];
                protocolAbstract = (o == DBNull.Value) ? string.Empty : (string)o;
            }

		// load the details too.
		if ( detailsLoaded )
			{
				o = dataRow["ResponsibleIRB"];
				responsibleIRB = (o == DBNull.Value) ? string.Empty : (string)o;	
				o = dataRow["LastModifierName"];
				lastModifierName = (o == DBNull.Value) ? string.Empty : (string)o;	
				o = dataRow["SubmissionDate"];
				submissionDate = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;
				o = dataRow["LastModifiedDate"];
				lastModifiedDate = (o == DBNull.Value) ? string.Empty : ((DateTime)o).ToString("MM/dd/yyyy");
				o = dataRow["Administrator"];
				administratorName = (o == DBNull.Value) ? string.Empty : (string)o;
				o = dataRow["EmailAddress"];
				adminEmail = (o == DBNull.Value) ? string.Empty : (string)o;	
				o = dataRow["PhoneNumber"];
				adminPhone = (o == DBNull.Value) ? string.Empty : (string)o;
				o = dataRow["ApprovalDate"];
				approvalDate = (o == DBNull.Value) ? string.Empty : ((DateTime)o).ToString("MM/dd/yyyy");
				fwkResourceId = (int) dataRow["FwkResourceId"];
				overallStatusId = (int) dataRow["OverallStatusId"];
				o = dataRow["SponsorId"];
				if (o != DBNull.Value) 
					sponsorId = (int)o;
				o = dataRow["ProtocolType"];
				protocolTypeCode = (o == DBNull.Value ) ? string.Empty : (string)o;
				o = dataRow["PIPersonId"];
				piPersonId = (o == DBNull.Value ) ? 0 : (int)o;
				o = dataRow["ResponsibleIRBOrganizationId"];
				responsibleIRBId = (o == DBNull.Value ) ? 0 : (int)o;
				o = dataRow["ProtocolAdminPersonId"];
				administratorId = (o == DBNull.Value ) ? 0 : (int)o;
				o = dataRow["ProtocolAdminFwkDomainUserId"];
				administratorDomainUserId = (o == DBNull.Value ) ? 0 : (int)o;
                o = dataRow["ProtocolPeriodStartDate"];
                protocolPeriodStartDate = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;
                o = dataRow["ProtocolPeriodEndDate"];
                protocolPeriodEndDate = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;
                o = dataRow["NumberOfAnimals"];
                numberOfAnimals = (o == DBNull.Value) ? 0 : (int)o;
                radioIsotope = (bool)dataRow["RadioIsotope"];
                biologicalAgent = (bool)dataRow["BiologicalAgent"];
                toxicChem = (bool)dataRow["ToxicChem"];
                tissueTest = (bool)dataRow["TissueTest"];
            }
		}

		/// <summary>
		/// Returns a new ProtocolProcess of the given type, and adds it to the ProtocolProcesses
		/// collection of the Protocol.
		/// </summary>
		/// <param name="processType">ProtocolProcessType enumeration- which type to add.</param>
		/// <param name="cachePolicy">the cachepolicy to construct the object with.</param>
		/// <returns></returns>
		public ProtocolProcess GetNewProcess( ProtocolProcessType processType, ICachePolicy cachePolicy,
						string connectionString )
		{
			ProtocolProcess newProcess;

			string processName;
			string typeRefCode;            

			switch( processType )
			{
				case ProtocolProcessType.Application :
					newProcess = new Application ( cachePolicy );
					processName = Humans.Constants.ProtocolProcessShortType.InitialReview;
					typeRefCode = Humans.Constants.ProtocolProcessType.InitialReview;
                    break;
				case ProtocolProcessType.Amendment  :
					newProcess = new Amendment (cachePolicy);
					processName = Humans.Constants.ProtocolProcessShortType.Amendment;
					typeRefCode = Humans.Constants.ProtocolProcessType.Amendment;                    
					break;
				case ProtocolProcessType.AdverseEvent :
					newProcess = new AdverseEvent( cachePolicy );
					processName = Humans.Constants.ProtocolProcessShortType.AdverseEvent;
					typeRefCode = Humans.Constants.ProtocolProcessType.AdverseEvent;                   
					break;
				case ProtocolProcessType.ContinuingReview :
					newProcess = new ContinuingReview ( cachePolicy );
					processName = Humans.Constants.ProtocolProcessShortType.ContinuingReview;
					typeRefCode = Humans.Constants.ProtocolProcessType.ContinuingReview;                    
					break;
				case ProtocolProcessType.OtherEvent :
					newProcess = new OtherEvent ( cachePolicy );
					processName = Humans.Constants.ProtocolProcessShortType.OtherEvent;
					typeRefCode = Humans.Constants.ProtocolProcessType.OtherEvent;                   
					break;
                case ProtocolProcessType.AnimalsApplication:
                    newProcess = new Application(cachePolicy);
                    newProcess.Type = Constants.AnimalProtocolProcessType.InitialReview;
                    processName = Humans.Constants.AnimalProtocolProcessShortType.InitialReview;
                    typeRefCode = Humans.Constants.AnimalProtocolProcessType.InitialReview;
                    break;
                case ProtocolProcessType.AnimalsAnnualReview:
                    newProcess = new ContinuingReview(cachePolicy);
                    newProcess.Type = Constants.AnimalProtocolProcessType.AnnualReview;
                    processName = Humans.Constants.AnimalProtocolProcessShortType.AnnualReview;
                    typeRefCode = Humans.Constants.AnimalProtocolProcessType.AnnualReview;
                    break;
                case ProtocolProcessType.AnimalsDeficiency:
                    newProcess = new AdverseEvent(cachePolicy);
                    newProcess.Type = Constants.AnimalProtocolProcessType.Deficiency;
                    processName = Humans.Constants.AnimalProtocolProcessShortType.Deficiency;
                    typeRefCode = Humans.Constants.AnimalProtocolProcessType.Deficiency;
                    break;
                case ProtocolProcessType.AnimalsTriennialReview:
                    newProcess = new ContinuingReview(cachePolicy);
                    newProcess.Type = Constants.AnimalProtocolProcessType.TriennialReview;
                    processName = Humans.Constants.AnimalProtocolProcessShortType.TriennialReview;
                    typeRefCode = Humans.Constants.AnimalProtocolProcessType.TriennialReview;
                    break;
                case ProtocolProcessType.AnimalsAmendment :
                    newProcess = new Amendment(cachePolicy);
                    newProcess.Type = Constants.AnimalProtocolProcessType.Amendment;
                    processName = Humans.Constants.AnimalProtocolProcessShortType.Amendment;
                    typeRefCode = Humans.Constants.AnimalProtocolProcessType.Amendment;
                    break;
				default :
					throw new Exception("Unrecognized Process Type");
			}

            if (processType == ProtocolProcessType.Application || processType == ProtocolProcessType.AnimalsApplication)
			{
				newProcess.Name = processName;
			}
			else
			{
				// Figure out the count of this type of process, and add it into the name
				int processCount = Protocol.GetWIPProcessCount ( typeRefCode, this.Id, connectionString );
				
				
				++processCount;
				
				//newProcess.Name = String.Format("{0}{1}", processName, processCount.ToString());
				newProcess.Name = String.Format("{0}-InProg-{1}", processName, processCount.ToString());

			}
			

			// set the ID and add to the collection.
			newProcess.LastModifiedDate = DateTime.Now;
			newProcess.ProtocolId = Id;
			newProcess.Status = Humans.Constants.ProtocolProcessStatus.WorkInProgress;
			ProtocolProcesses.Add (newProcess);
			
			return newProcess;
		}
		#endregion

		#region Static Methods 
		/// <summary>
		/// Gets the processnames (amendment or initial review) for a given protocol
		/// </summary>
		/// <param name="protocolId">Id of the protocol</param>
		/// <param name="cachePolicy">prevalent cache policy</param>
		/// <returns></returns>
		public static DataTable GetProcessIds (int protocolId, ICachePolicy cachePolicy)
		{
			return Utility.DataAccess.GetDataTable(Utility.DataAccess.ConnectionString, "GetProtocolFormsIds", 
				Utility.DataAccess.BuildSqlParameterArray
				("@protocolId", SqlDbType.Int, protocolId));
		}

		/// <summary>
		/// Loads all ProtocolProcesses for a given protocol Id
		/// </summary>
		/// <param name="protocolId">ID of the Protocol to load the processes for</param>
		/// <returns>loaded ArrayList or processes</returns>
		public static ArrayList GetProtocolProcesses ( int protocolId, ICachePolicy cachePolicy )
		{
			ArrayList processes = new ArrayList();

			DataSet processesDataSet = Utility.DataAccess.GetDataSet( Utility.DataAccess.ConnectionString,
				"GetProtocolProcesses",
				Utility.DataAccess.BuildSqlParameterArray
				("@protocolId", SqlDbType.Int, protocolId));

			// create a new Process for each row
			DataTable table = processesDataSet.Tables[0];

			for( int i=0; i < table.Rows.Count; ++i)
				processes.Add( ProtocolProcess.GetProtocolProcess ( Utility.DataAccess.GetConnectionString(),
					0, cachePolicy, table.Rows[i]) );			
			return processes;
		}
			/// <summary>
			/// Loads all ProtocolDocuments of the required type for a given protocol Id
			/// To Do:Change stored procedure
			/// </summary>
			/// <param name="protocolId">ID of the Protocol to load the processes for</param>
			/// <param name="protocolId">Document type to be fetched</param>
			/// <returns>loaded ArrayList or documents</returns>
		public static ArrayList GetProtocolDocuments ( int protocolId,string attachmentType ,ICachePolicy cachePolicy )
		{
			ArrayList protocolDocuments=null; 

			DataTable documentDataTable = Utility.DataAccess.GetDataTable(Utility.DataAccess.ConnectionString,
				"GetProtocolDocuments",	Utility.DataAccess.BuildSqlParameterArray(
				"@protocolId"		, SqlDbType.Int			, protocolId
				,"@attachType"	, SqlDbType.NVarChar	, attachmentType));
			int count=documentDataTable.Rows.Count;
			protocolDocuments=(count != 0)? new ArrayList():protocolDocuments;
			for( int i=0; i	< count; ++i)
			{			
				protocolDocuments.Add(new ProtocolDocument(cachePolicy,documentDataTable.Rows[i],false)); 
			}
			return protocolDocuments;
		}

		/// <summary>
		/// Returns only the electronic list of protocol documents, instead of all documents.
		/// </summary>
		/// <param name="protocolId">Id of the protocol</param>
		/// <param name="attachmentType"Document type to be fetched</param>
		/// <param name="cachePolicy">prevalent cache poluicy</param>
		/// <returns>Array list of electronic documents</returns>
		public static ArrayList GetPublishedConsentForms ( int protocolId,string attachmentType ,ICachePolicy cachePolicy )
		{
			ArrayList protocolDocuments=null; 

			DataTable documentDataTable = Utility.DataAccess.GetDataTable(Utility.DataAccess.ConnectionString,
				"GetProtocolDocuments",	Utility.DataAccess.BuildSqlParameterArray(
				"@protocolId"		, SqlDbType.Int			, protocolId
				,"@attachType"	, SqlDbType.NVarChar	, attachmentType));
			int count=documentDataTable.Rows.Count;
			protocolDocuments=(count != 0)? new ArrayList():protocolDocuments;
			for( int i=0; i	< count; ++i)
			{			
				ProtocolDocument doc = new ProtocolDocument(cachePolicy,documentDataTable.Rows[i], false);
				
				if ( doc.IsElectronicFile && doc.Published )
					protocolDocuments.Add(doc); 
			}
			return protocolDocuments;
		}

		/// <summary>
		/// Returns only the electronic list of protocol documents, instead of all documents.
		/// </summary>
		/// <param name="protocolId">Id of the protocol</param>
		/// <param name="attachmentType"Document type to be fetched</param>
		/// <param name="cachePolicy">prevalent cache poluicy</param>
		/// <returns>Array list of electronic documents</returns>
		public static ArrayList GetElectronicOnlyProtocolDocuments ( int protocolId,string attachmentType ,ICachePolicy cachePolicy )
		{
			ArrayList protocolDocuments=null; 

			DataTable documentDataTable = Utility.DataAccess.GetDataTable(Utility.DataAccess.ConnectionString,
				"GetProtocolDocuments",	Utility.DataAccess.BuildSqlParameterArray(
				"@protocolId"		, SqlDbType.Int			, protocolId
				,"@attachType"	, SqlDbType.NVarChar	, attachmentType));
			int count=documentDataTable.Rows.Count;
			protocolDocuments=(count != 0)? new ArrayList():protocolDocuments;
			for( int i=0; i	< count; ++i)
			{			
				ProtocolDocument doc = new ProtocolDocument(cachePolicy,documentDataTable.Rows[i], false);
				
				if ( doc.IsElectronicFile )
					protocolDocuments.Add(doc); 
			}
			return protocolDocuments;
		}

        /// <summary>
		/// Gets the latest version of the protocol application . Returns null if no IR exists.
		/// </summary>
		/// <param name="protocolId">id of the protocol to get latest version of</param>
		/// <param name="connectionString">string to connect to the database</param>
		/// <param name="forModification">boolean to determine if the version will be modified or not 
		///  (passed down into the Process function)</param>
		/// <param name="cachePolicy"></param>
        /// <param name="moduleType"></param>
		/// <returns>latest protocol version</returns>
		public static ProtocolVersion GetLatestApplicationVersion(int protocolId, 
			string connectionString, bool forModification, ICachePolicy cachePolicy)
		{
			// todo: pass in null cachepolicy
			ArrayList processes = GetProtocolProcesses(protocolId, cachePolicy);

            Protocol protocol = new Protocol(cachePolicy);
            protocol.Load(connectionString, protocolId);

            string processTypeIR = (protocol.ModuleType == Constants.ModuleType.Humans) ? Constants.ProtocolProcessType.InitialReview : Constants.AnimalProtocolProcessType.InitialReview;

            foreach (ProtocolProcess process in processes)
			{
				if (process.Type == processTypeIR)
					return process.GetLatestProtocolVersion(connectionString, forModification, cachePolicy);
			}

			// todo: return new protocolversion for legacy protocols?
			return null;
		}

		/// <summary>
		/// Gets thea pproved protocol Version.
		/// </summary>
		/// <param name="protocolId"></param>
		/// <param name="connectionString"></param>
		/// <param name="forModification"></param>
		/// <param name="cachePolicy"></param>
		/// <returns></returns>
		public static ProtocolVersion GetOriginalIRVersion(int protocolId, 
			string connectionString, ICachePolicy cachePolicy)
		{
			ArrayList processes = GetProtocolProcesses(protocolId, cachePolicy);

			foreach (ProtocolProcess process in processes)
			{
                if (process.Type == Constants.ProtocolProcessType.InitialReview || process.Type == Constants.AnimalProtocolProcessType.InitialReview)
				{
					Application app = (Application)process;
					return app.GetInitiallyApprovedVersion( );
				}
		
			}

			// todo: return new protocolversion for legacy protocols?
			return null;
		}

		/// <summary>
		/// Retrieves the different levels of access possible for a person to have for a protocol, 
		/// consisting of a bitvalue and a name for the access (e.g. View, Edit, Manage)
		/// </summary>
		/// <param name="connectionString">string containing database connection info</param>
		/// <returns>DataTable containing access levels</returns>
		public static DataTable GetAccessLevels(string connectionString, string moduleType)
		{
			try
			{
				return DataAccess.GetDataSet(connectionString, "GetProtocolAccessLevels",DataAccess.BuildSqlParameterArray(
                    "@moduleType", SqlDbType.Char, moduleType)).Tables[0];
			}
			catch(Exception e)
			{
				Utility.ExceptionReport.WriteToEventLog("StudyStaff GetAccessLevels", e, true, Utility.ExceptionReport.ReportType.Error);
				throw e;
			}
		}
		
		/// <summary>
		/// 
		/// </summary>
		/// <param name="protocolDocument">Protocol documnet for which the document needs to be added</param>
		/// <returns>Document object </returns>
		/// 


		/// <summary>
		/// Get the count of the passed in type for the given protocol. This helps to determine the
		/// "FormsID" aka the Name of the Process, ex. AM3
		/// </summary>
		/// <param name="processType">the type of process to get a count for</param>
		/// <param name="protocolId">the protocol whose count you want</param>
		/// <param name="connectionString">database connection string.</param>
		/// <returns></returns>
		public static int GetProcessCount ( string processType, int protocolId, string connectionString)
		{
			object o = Utility.DataAccess.ExecuteScalar( connectionString, "GetProcessCount", 
								Utility.DataAccess.BuildSqlParameterArray( 
									"@protocolId", SqlDbType.Int, protocolId,
									"@processType", SqlDbType.VarChar, processType));
			if ( o != null && o != DBNull.Value )
				return (int)o;
			else return 0;
		}

		/// <summary>
		/// Get the count of the passed in type for the given protocol. This helps to determine the
		/// "FormsID" aka the Name of the Process, ex. AME-W3
		/// </summary>
		/// <param name="processType">the type of process to get a count for</param>
		/// <param name="protocolId">the protocol whose count you want</param>
		/// <param name="connectionString">database connection string.</param>
		/// <returns></returns>
		public static int GetWIPProcessCount ( string processType, int protocolId, string connectionString)
		{
			object o = Utility.DataAccess.ExecuteScalar( connectionString, "GetWIPProcessCount", 
				Utility.DataAccess.BuildSqlParameterArray( 
				"@protocolId", SqlDbType.Int, protocolId,
				"@processType", SqlDbType.VarChar, processType));
			if ( o != null && o != DBNull.Value )
				return (int)o;
			else return 0;
		}


        /// <summary>
        /// Find the type of protocol
        /// </summary>
        /// <param name="connectionString">Connection string to the database<</param>
        /// <param name="args">additional arguments</param>
        private string GetProposalNumberByProtocolNumber(params object[] args)
        {

            string strProposalNumber = "";
            DataTable dt = Utility.DataAccess.GetDataTable(
                DataAccess.GetConnectionString(),
                "GetInfoEdProposalNumberByProtocolNumber",
                Utility.DataAccess.BuildSqlParameterArray("@ProtocolNumber", SqlDbType.VarChar, args[0]));

            if (null != dt && dt.Rows.Count > 0)
            {
                strProposalNumber = (string.Empty != dt.Rows[0]["AgreementFolderNumber"].ToString()) ? dt.Rows[0]["AgreementFolderNumber"].ToString() : "";
            }

            return strProposalNumber;
        }

		#endregion

		#region Public methods
		/// <summary>
		/// Adds a sponsor to the protocol and makes it a secondary sponsor.
		/// </summary>
		/// <param name="connectionString">Database connection information</param>
		/// <param name="spId">sponsor id</param>
		public void SaveSponsorMap(string connectionString, ArrayList ids)
		{
			try
			{
				DataAccess.ExecuteNonQuery(connectionString, "DeleteAllProtocolAdmSponsorMaps", 
					DataAccess.BuildSqlParameterArray("@protocolId", SqlDbType.Int, Id));

            foreach (int id in ids)
            {
               if (id > 0)
               {
                  DataAccess.ExecuteNonQuery(connectionString, "AddProtocolAdmSponsorMap",
                     DataAccess.BuildSqlParameterArray(
                     "@protocolId", SqlDbType.Int, Id,
                     "@admSponsorId", SqlDbType.Int, id));
               }
            }
			}
			catch(Exception ex)
			{
            Utility.ExceptionReport.WriteToEventLog("Protocol.SaveSponsorMap Failed", ex, true, Utility.ExceptionReport.ReportType.Error);
				throw;
			}
		}

		/// <summary>
		/// Adds a StaffPerson to the protocol and sets current flag to true.
		/// StudyStaff object gets saved after changes.		
		/// </summary>
		/// <param name="StaffPerson">StaffPerson Object</param>
		public void AddStudyStaff(StaffPerson staffPerson)
		{
			try
			{				
				staffPerson.ProtocolId = Id;
				staffPerson.IsCurrent = true;
                
				StudyStaffList.Add(staffPerson);
			}
			catch (Exception ex)
			{
				throw new Exception("Failed to add study staff", ex);
			}
		}

		/// <summary>
		/// Removes a StaffPerson from the protocol.
		/// StudyStaff object gets saved after changes.	 Note that the staff person will
		/// be permanently deleted from the database (not just marked for deletion)
		/// </summary>
		/// <param name="connectionString">connection to the DB.</param>
		/// <param name="StaffPerson">StaffPerson Object</param>
		public void RemoveStudyStaff(string connectionString, StaffPerson staffPerson)
		{
			try
			{				
				StudyStaffList.Remove(staffPerson);
				staffPerson.Delete(connectionString);
			}
			catch (Exception ex)
			{
				throw new Exception("Failed to add study staff", ex);
			}
		}

		/// <summary>
		/// Updates staff changes in REX.  The PI is not updated
		/// </summary>
		/// <param name="insightConnectionString">string containing
		/// info to connect to Insight db</param>
		/// <param name="rexConnectionString">string containing
		/// info to connect to REX db</param>
		public void UpdateStaffInREX(string insightConnectionString, 
			string rexConnectionString)
		{
			SqlConnection rexConn = new SqlConnection(rexConnectionString);
			SqlTransaction rexTransaction = null;
			try
			{
				int rexProtocolId = RexProtocolId;

				rexConn.Open();
				rexTransaction = rexConn.BeginTransaction();

                if (this.ModuleType == Humans.Constants.ModuleType.Humans)
                {
                    // Rex removes the staff to do its updates, so remove the staff first, then re-add them all.
                    Utility.DataAccess.ExecuteNonQuery(rexTransaction.Connection, rexTransaction,
                        "spIRBStudyStaffDelete",
                        Utility.DataAccess.BuildSqlParameterArray(
                        "@nIRBProtocolID", SqlDbType.Int, rexProtocolId));
                }
                else
                {
                    Utility.DataAccess.ExecuteNonQuery(rexTransaction.Connection, rexTransaction,
                       "spAnimalStudyStaffDelete",
                       Utility.DataAccess.BuildSqlParameterArray(
                       "@nAnimalProtId", SqlDbType.Int, rexProtocolId));
                }

				// now move the study staff over to Rex, if they're not already there.
				for ( int i = 0, count = StudyStaffList.Count; i < count; ++i )
				{
					StaffPerson staff = (StaffPerson)StudyStaffList[i];

                    if (staff.IsCurrent == false)
                        continue;
					
                    // don't move over the PI for Humans.
					if ((staff.RoleId != 
						Humans.Constants.StaffRoleType.PrincipalInvestigator && this.ModuleType == Humans.Constants.ModuleType.Humans )
                        || (this.ModuleType == Humans.Constants.ModuleType.Animals ))
					{
						if ( staff.Person.RexRecomId == 0 )
						{	
							staff.Person.CreateRecomRecord(insightConnectionString, 
								rexConnectionString);
						}

                        if (this.ModuleType == Humans.Constants.ModuleType.Humans)
                        {

                            // HUMANS
                            int rexStudyStaffId =
                                Convert.ToInt32(Utility.DataAccess.ExecuteScalar(
                                rexTransaction.Connection,
                                rexTransaction,
                                "spIRBStudyStaffUpdate",
                                Utility.DataAccess.BuildSqlParameterArray(
                                "@nIRBProtocolId", SqlDbType.Int, rexProtocolId,
                                "@nRecomAbstractID", SqlDbType.Int, staff.Person.RexRecomId,
                                "@sName", SqlDbType.VarChar, string.Empty, // not used, but Rex needs it...
                                "@sPosition", SqlDbType.VarChar, staff.RexRoleName,
                                "@bGetsMail", SqlDbType.Bit, staff.IsContactPerson,
                                "@sEmailAddress", SqlDbType.VarChar, staff.Person.Email
                                )));
                        }
                        else
                        {
                            // ANIMALS
                            int rexStudyStaffId =
                               Convert.ToInt32(Utility.DataAccess.ExecuteScalar(
                               rexTransaction.Connection,
                               rexTransaction,
                               "spAnimalStudyStaffUpdate",
                               Utility.DataAccess.BuildSqlParameterArray(
                               "@nAnimalProtId", SqlDbType.Int, rexProtocolId,
                               "@nRecomAbstractID", SqlDbType.Int, staff.Person.RexRecomId,
                               "@sPosition", SqlDbType.VarChar, staff.Role,
                               "@bGetsMail", SqlDbType.Bit, staff.IsContactPerson,
                               "@sEmailAddress", SqlDbType.VarChar, staff.Person.Email
                               )));
                        }
                              
					}
					else 
						// Update the Protocol record with the PI Only for Rex.
						UpdatePIInREX(insightConnectionString, rexConnectionString,
							rexTransaction, staff.Person.RexRecomId);
				}

                // move non study staff over to Rex, if they're not already there.
                for (int i = 0, count = nonStudyStaffList.Count; i < count; ++i)
                {
                    NonStaffPerson nonStaff = (NonStaffPerson)nonStudyStaffList[i];

                    if (this.ModuleType == Humans.Constants.ModuleType.Humans)
                    {
                        if (nonStaff.IsContactPerson)
                        {
                            if (nonStaff.Person.RexRecomId == 0)
                            {
                                nonStaff.Person.CreateRecomRecord(insightConnectionString,
                                    rexConnectionString);
                            }

                            if (this.ModuleType == Humans.Constants.ModuleType.Humans)
                            {
                                int rexStudyStaffId =
                                    Convert.ToInt32(Utility.DataAccess.ExecuteScalar(
                                    rexTransaction.Connection,
                                    rexTransaction,
                                    "spIRBStudyStaffUpdate",
                                    Utility.DataAccess.BuildSqlParameterArray(
                                    "@nIRBProtocolId", SqlDbType.Int, rexProtocolId,
                                    "@nRecomAbstractID", SqlDbType.Int, nonStaff.Person.RexRecomId,
                                    "@sName", SqlDbType.VarChar, string.Empty, // not used, but Rex needs it...
                                    "@sPosition", SqlDbType.VarChar, "M-Gets Mail",
                                    "@bGetsMail", SqlDbType.Bit, nonStaff.IsContactPerson,
                                    "@sEmailAddress", SqlDbType.VarChar, nonStaff.Person.Email
                                    )));
                            }
                        }
                    }
                }
				rexTransaction.Commit();
			}
			catch(Exception ex)
			{
				rexTransaction.Rollback();
				throw new Exception("Staff Updates in REX failed: ", ex);
			}
			finally
			{
				if (rexConn.State != ConnectionState.Closed)
					rexConn.Close();
			}
		}       

        /// <summary>
        /// Updates non study staff changes in REX. 
        /// </summary>
        /// <param name="insightConnectionString">string containing
        /// info to connect to Insight db</param>
        /// <param name="rexConnectionString">string containing
        /// info to connect to REX db</param>
        public virtual void UpdateNonStudyStaffInREX(string insightConnectionString, string rexConnectionString, Protocol protocol)
        {
            SqlConnection rexConn = new SqlConnection(rexConnectionString);
            SqlTransaction rexTransaction = null;

            try
            {
                rexConn.Open();
                rexTransaction = rexConn.BeginTransaction();

                int rexProtocolId = protocol.RexProtocolId;

                for (int i = 0, count = NonStudyStaff.Count; i < count; ++i)
                {
                    NonStaffPerson nonStaff = (NonStaffPerson)NonStudyStaff[i];
                    
                    if (this.ModuleType == Humans.Constants.ModuleType.Humans)
                    {                       
                        // Rex removes the staff to do its updates, so remove the staff first, then re-add them all.
                        Utility.DataAccess.ExecuteNonQuery(rexTransaction.Connection, rexTransaction,
                            "spIRBStudyStaffDelete",
                            Utility.DataAccess.BuildSqlParameterArray(
                            "@nIRBProtocolID", SqlDbType.Int, rexProtocolId,
                            "@nRECOMAbstractID", SqlDbType.Int, nonStaff.Person.RexRecomId));                        

                        if (nonStaff.IsContactPerson)
                        {
                            if (nonStaff.Person.RexRecomId == 0)
                            {
                                nonStaff.Person.CreateRecomRecord(insightConnectionString,
                                    rexConnectionString);
                            }
                            int rexStudyStaffId =
                                Convert.ToInt32(Utility.DataAccess.ExecuteScalar(
                                rexTransaction.Connection,
                                rexTransaction,
                                "spIRBStudyStaffUpdate",
                                Utility.DataAccess.BuildSqlParameterArray(
                                "@nIRBProtocolId", SqlDbType.Int, rexProtocolId,
                                "@nRecomAbstractID", SqlDbType.Int, nonStaff.Person.RexRecomId,
                                "@sName", SqlDbType.VarChar, string.Empty, // not used, but Rex needs it...
                                "@sPosition", SqlDbType.VarChar, "M-Gets Mail",
                                "@bGetsMail", SqlDbType.Bit, nonStaff.IsContactPerson,
                                "@sEmailAddress", SqlDbType.VarChar, nonStaff.Person.Email
                                )));
                        }
                    }
                }

                rexTransaction.Commit();

            }
            catch (Exception ex)
            {
                rexTransaction.Rollback();
                throw new Exception("Non Study Staff Updates in REX failed", ex);
            }
            finally
            {
                if (rexConn.State != ConnectionState.Closed)
                    rexConn.Close();
            }
        }

        /// <summary>
        /// Updates non study staff changes in REX. 
        /// </summary>
        /// <param name="insightConnectionString">string containing
        /// info to connect to Insight db</param>
        /// <param name="rexConnectionString">string containing
        /// info to connect to REX db</param>
        public virtual void UpdateSingleNonStudyStaffInREX(string insightConnectionString, string rexConnectionString, Protocol protocol, int personId, bool IsContact)
        {
            SqlConnection rexConn = new SqlConnection(rexConnectionString);
            SqlTransaction rexTransaction = null;

            try
            {
                rexConn.Open();
                rexTransaction = rexConn.BeginTransaction();

                int rexProtocolId = protocol.RexProtocolId;

                for (int i = 0, count = NonStudyStaff.Count; i < count; ++i)
                {
                    NonStaffPerson nonStaff = (NonStaffPerson)NonStudyStaff[i];

                    if (nonStaff.Id == personId)
                        {
                            if (this.ModuleType == Humans.Constants.ModuleType.Humans)
                            {
                        
                            // Rex removes the staff to do its updates, so remove the staff first, then re-add them all.
                            Utility.DataAccess.ExecuteNonQuery(rexTransaction.Connection, rexTransaction,
                                "spIRBStudyStaffDelete",
                                Utility.DataAccess.BuildSqlParameterArray(
                                "@nIRBProtocolID", SqlDbType.Int, rexProtocolId,
                                "@nRECOMAbstractID", SqlDbType.Int, nonStaff.Person.RexRecomId));

                            if (IsContact)
                            {
                                if (nonStaff.Person.RexRecomId == 0)
                                {
                                    nonStaff.Person.CreateRecomRecord(insightConnectionString,
                                        rexConnectionString);
                                }
                                int rexStudyStaffId =
                                    Convert.ToInt32(Utility.DataAccess.ExecuteScalar(
                                    rexTransaction.Connection,
                                    rexTransaction,
                                    "spIRBStudyStaffUpdate",
                                    Utility.DataAccess.BuildSqlParameterArray(
                                    "@nIRBProtocolId", SqlDbType.Int, rexProtocolId,
                                    "@nRecomAbstractID", SqlDbType.Int, nonStaff.Person.RexRecomId,
                                    "@sName", SqlDbType.VarChar, string.Empty, // not used, but Rex needs it...
                                    "@sPosition", SqlDbType.VarChar, "M-Gets Mail",
                                    "@bGetsMail", SqlDbType.Bit, IsContact,
                                    "@sEmailAddress", SqlDbType.VarChar, nonStaff.Person.Email
                                    )));
                            }
                        }
                    }
                }
                rexTransaction.Commit();
                }
            catch (Exception ex)
            {
                rexTransaction.Rollback();
                throw new Exception("Non Study Staff Updates in REX failed", ex);
            }
            finally
            {
                if (rexConn.State != ConnectionState.Closed)
                    rexConn.Close();
            }
        }

		/// <summary>
		/// Updates the Protocol record in REX with PI information
		/// </summary>
		/// <param name="insightConnectionString">string containing
		/// info to connect to Insight db</param>
		/// <param name="rexConnectionString">string containing
		/// info to connect to REX db</param>
		/// <param name="protocol">protocol to udpate</param>
		/// <param name="rexTransaction">transaction to use to perform
		/// the update</param>
		private void UpdatePIInREX(string insightConnectionString, 
			string rexConnectionString,	SqlTransaction rexTransaction, int rexRecomId)
		{
			CDS.Person PI = new CDS.Person(CachePolicy);
			PI.Load (insightConnectionString, PIPersonId );

			// check the PI has a Recom ID since Rex requires it.
			if (PI != null && PI.RexRecomId == 0 )
				PI.CreateRecomRecord(insightConnectionString, rexConnectionString );

			CDS.Organization PIOrg = new PHSRAG.Insight21.CDS.Organization( CachePolicy );
			PIOrg.Load ( insightConnectionString, PI.OrganizationId );
			// create Institution, Department and Unit Orgs for the PI's Org.
			Organization PIOrgInstitution = new PHSRAG.Insight21.CDS.Organization(CachePolicy);
			PIOrgInstitution.Load(insightConnectionString, PIOrg.InstitutionId);
			Organization PIOrgDept = new PHSRAG.Insight21.CDS.Organization(CachePolicy);
			PIOrgDept.Load(insightConnectionString, PIOrg.DepartmentId);
			Organization PIOrgUnit = new PHSRAG.Insight21.CDS.Organization(CachePolicy);
			PIOrgUnit.Load(insightConnectionString, PIOrg.UnitId);
					
			string unitName = 
				(PIOrgUnit == null || PIOrgUnit.Name == null ||
				PIOrgUnit.Name == string.Empty) ? "(None)" :
				String.Format("{0} {1}", PIOrgUnit.Name,PIOrgUnit.ChiefCode);

			if( PIOrgDept.Name != null )
			{
				Utility.DataAccess.ExecuteScalar( 
					rexTransaction.Connection, 
					rexTransaction,
					"spIRBPIInfoUpdate",
					Utility.DataAccess.BuildSqlParameterArray(
					"@sFolderNumber", SqlDbType.VarChar, ProtocolNumber,
					"@nPIID", SqlDbType.Int, rexRecomId,
					"@sPIInstitution", SqlDbType.VarChar, 
					String.Format("{0} {1}", PIOrgInstitution.Name, PIOrgInstitution.ChiefCode),
					"@sPIDept", SqlDbType.VarChar, 
					String.Format("{0} {1}", PIOrgDept.Name, PIOrgDept.ChiefCode), 
					"@sPIUnit", SqlDbType.VarChar, unitName));
			}
		}
		
		#endregion

		#region Private methods
		/// <summary>
		/// Adds a new protocol document to the ProtocolDocuments collection
		/// </summary>
		/// <param name="title">Title of the attachment</param>
		/// <param name="type">Type of the attachment</param>
		/// <param name="refMeaning">Visible description of the type</param>
		/// <param name="isHardCopy">Indicates whether or not the attachment is a hardcopy or not</param>
		/// <returns>The new ProtocolDocument object added to the container</returns>
		public virtual ProtocolDocument AddProtocolDocument(string title, string type, string refMeaning, bool isHardCopy)
		{
			ProtocolDocument newAttachmentType = new ProtocolDocument(CachePolicy);
			newAttachmentType.Id = 0;
			newAttachmentType.ProtocolId = Id;
			newAttachmentType.Type = type;
			newAttachmentType.RefMeaning = refMeaning;
			newAttachmentType.Name = title;
			newAttachmentType.IsHardCopy = isHardCopy;
			newAttachmentType.AddedProtocolProcessId = 0;
			newAttachmentType.LastLockedVersion = 0;
			newAttachmentType.IsCurrent = true;
			ProtocolDocuments.Add(newAttachmentType);

			return newAttachmentType;
		}


		/// <summary>
		/// Removes the protocol sponsor map
		/// </summary>
		/// <param name="sqlTransaction">sql transaction in which the statement is to be executed</param>
		/// <param name="protocolId">Id of the Protocol</param>
		/// <param name="admSponsorId">Id of the sponsor</param>
		private void DeleteProtocolAdmSponsorMap(SqlTransaction sqlTransaction, int protocolId, int admSponsorId)
		{
			DataAccess.ExecuteNonQuery(sqlTransaction.Connection, sqlTransaction, "DeleteProtocolAdmSponsorMap", 
				DataAccess.BuildSqlParameterArray(
				"@protocolId",				SqlDbType.Int,			protocolId,
				"@admSponsorId",			SqlDbType.Int,		admSponsorId));
		}

		/// <summary>
		/// UpdateTitleVersionInRex updates the title and version of the protocol in Rex.
		/// </summary>
		/// <param name="rexConnection">rex DB connection</param>
		/// <param name="rexTransaction">Open rex DB Transaction</param>
		public void UpdateTitleVersionInRex ( SqlConnection rexConnection, SqlTransaction rexTransaction)
		{
			Utility.DataAccess.ExecuteScalar( rexConnection, rexTransaction,
				"spIRBProtocolInfoUpdate",
				Utility.DataAccess.BuildSqlParameterArray(
				"@sFolderNumber", SqlDbType.VarChar, ProtocolNumber,
				"@sProtocolTitle", SqlDbType.VarChar, Title,
				"@sVersionName", SqlDbType.VarChar, Version,
                "@sModuleType",SqlDbType.VarChar, moduleType));
		}
		#endregion

		#region Protocol Change Event Wire-ups

		/// <summary>
		/// Delegate used by entity that wishes to be informed of the protocol change event
		/// </summary>
		public delegate void ChangeEventHandler();

		/// <summary>
		/// Add/remove a handler for the "Protocol has changed" command
		/// </summary>
		public event ChangeEventHandler Change
		{
			add		{ events.AddHandler(ChangeCommand, value); }
			remove	{ events.RemoveHandler(ChangeCommand, value); }
		}

		#endregion
	}
}
